//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

s8 wl_keyboard_req(struct client *c,s32 keyboard_slot,struct msg *m)
{
  s8 r=LWL_OK;
  switch(m->req_op){
  case WL_KEYBOARD_CLOSE:
    LOG_WIRE("client(%d):keyboard::close keyboard_id=%u\n",c->so,m->req[0]);
    r=keyboard_close(c,keyboard_slot);
    break;
  default:
    PERR("client(%d):fatal:keyboard unknown opcode %u\n",c->so,m->req_op);
    r=LWL_ERR;
  }
  return r;
}

#define EV_PAYLOAD_KEYMAP_DWS 4
s8 wl_keyboard_ev_keymap(struct client *c,u32 keyboard_id,u32 fmt,i fd,u32 sz)
{
  u32 ev[EV_PAYLOAD_KEYMAP_DWS];

  ev[0]=keyboard_id;
  u16 ev_sz=EV_PAYLOAD_KEYMAP_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_KEYBOARD_KEYMAP;
  ev[2]=fmt;
  ev[3]=sz;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  u8 cmsg_buf[CMSG_SPACE(sizeof(fd))];
  memset(&msg,0,sizeof(msg));
  memset(cmsg_buf,0,sizeof(cmsg_buf));

  msg.ctl=cmsg_buf;
  msg.ctl_len=sizeof(cmsg_buf);

  struct cmsg_hdr *cmsg=CMSG_FIRSTHDR(&msg);
  cmsg->lvl=SOL_SOCKET;
  cmsg->type=SCM_RIGHTS;
  cmsg->len=CMSG_LEN(sizeof(fd));

  msg.iov=&iov;
  msg.iov_len=1;
  *((i*)(CMSG_DATA(CMSG_FIRSTHDR(&msg))))=fd;

  LOG_WIRE("client(%d):keyboard::keymap keyboard_id=%u fd=%d sz=%u\n",c->so,
                                                             keyboard_id,fd,sz);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):keyboard::keymap unable to send event\n",c->so,
                                                                             r);
    r=LWL_ERR;goto exit;
  }
  if(r!=ev_sz){
    PERR("client(%d):fatal:keyboard::keymap only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_BUF_SZ PAGE_SZ
s8 wl_keyboard_ev_enter(struct client *c,u32 keyboard_id,u32 serial,
                                                       u32 surface_id,u32 *keys)
{
  //Static storage to avoid the stack. The page size is a power of 2.
  static u32 ev[EV_BUF_SZ>>2];

  u16 ev_sz=4+4+4+4+4+*keys;
  l r;
  if(ev_sz>EV_BUF_SZ){
    PERR("client(%d):fatal:keyboard::enter array too big(%u) for buffer(%u)\n",
                                                           c->so,ev_sz,PAGE_SZ);
    r=LWL_ERR;goto exit;
  }

  ev[0]=keyboard_id;
  ev[1]=ev_sz<<16|WL_KEYBOARD_ENTER;
  ev[2]=serial;
  ev[3]=surface_id;
  memcpy(&ev[4],keys,*keys+4);

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):keyboard::enter keyboard_id=%u serial=%u surface_id=%u"
                "keys_array_sz=%u\n",c->so,keyboard_id,serial,surface_id,*keys);

  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):keyboard::enter unable to send event\n",c->so,
                                                                             r);
    r=LWL_ERR;goto exit;
  }
  if(r!=ev_sz){
    PERR("client(%d):fatal:keyboard::enter only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;
  
exit:
  return (s8)r;
}

#define EV_LEAVE_DWS 4
s8 wl_keyboard_ev_leave(struct client *c,u32 keyboard_id,u32 serial,
                                                                 u32 surface_id)
{
  u32 ev[EV_LEAVE_DWS];

  ev[0]=keyboard_id;
  u16 ev_sz=EV_LEAVE_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_KEYBOARD_LEAVE;
  ev[2]=serial;
  ev[3]=surface_id;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):keyboard::leave keyboard_id=%u serial=%u"
                        " surface_id=%u\n",c->so,keyboard_id,serial,surface_id);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):keyboard::leave unable to send event\n",c->so,
                                                                             r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:keyboard::leave only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_KEY_DWS 6
s8 wl_keyboard_ev_key(struct client *c,u32 keyboard_id,u32 serial,u32 time,
                                                              u32 key,u32 state)
{
  u32 ev[EV_KEY_DWS];

  ev[0]=keyboard_id;
  u16 ev_sz=EV_KEY_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_KEYBOARD_KEY;
  ev[2]=serial;
  ev[3]=time;
  ev[4]=key;
  ev[5]=state;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):keyboard::key keyboard_id=%u serial=%u time=%u key=%u"
                          "state=%u\n",c->so,keyboard_id,serial,time,key,state);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):keyboard::key unable to send event\n",c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:keyboard::key only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_MODIFIERS_DWS 7
s8 wl_keyboard_ev_modifiers(struct client *c,u32 keyboard_id,u32 serial,
                  u32 mods_depressed,u32 mods_latched,u32 mods_locked,u32 group)
{
  u32 ev[EV_MODIFIERS_DWS];

  ev[0]=keyboard_id;
  u16 ev_sz=EV_KEY_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_KEYBOARD_MODIFIERS;
  ev[2]=serial;
  ev[3]=mods_depressed;
  ev[4]=mods_latched;
  ev[5]=mods_locked;
  ev[6]=group;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):keyboard::modifiers keyboard_id=%u serial=%u"
                  "mods_depressed=0x%08x mods_latched=0x%08x mods_locked=0x%08x"
                      " group=0x%08x\n",c->so,keyboard_id,serial,mods_depressed,
                                                mods_latched,mods_locked,group);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):keyboard::modifiers unable to send event\n",
                                                                       c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:keyboard::modifiers only %ld/%ld were sent\n",c->so,
                                                                       r,ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}
